use crate::entities::{language};
use sea_orm::entity::prelude::*;
use crate::errors::{AppError, Result};
use crate::models::{
    Language, CreateLanguageRequest, UpdateLanguageRequest, FilterOptions, PaginatedResponse,
    Pagination
};
use sea_orm::*;
use uuid::Uuid;

pub struct LanguageService;

impl LanguageService {
    pub async fn create(
        db: &DatabaseConnection,
        request: CreateLanguageRequest,
    ) -> Result<Language> {
        let existing_default = language::Entity::find()
            .filter(language::Column::IsDefault.eq(true))
            .one(db)
            .await?;

        let is_default = request.is_default && existing_default.is_none();

        let new_language = language::ActiveModel {
            id: Set(Uuid::new_v4()),
            code: Set(request.code),
            name: Set(request.name),
            is_default: Set(is_default),
            is_active: Set(true),
            created_at: Set(chrono::Utc::now()),
            updated_at: Set(chrono::Utc::now()),
        };

        let language = new_language.insert(db).await?;

        Ok(Language {
            id: language.id,
            code: language.code,
            name: language.name,
            is_default: language.is_default,
            is_active: language.is_active,
        })
    }

    pub async fn get_by_id(db: &DatabaseConnection, id: Uuid) -> Result<Language> {
        let language = language::Entity::find_by_id(id)
            .one(db)
            .await?
            .ok_or_else(|| AppError::NotFound {
                entity: "Language".to_string(),
                id,
            })?;

        Ok(Language {
            id: language.id,
            code: language.code,
            name: language.name,
            is_default: language.is_default,
            is_active: language.is_active,
        })
    }

    pub async fn get_by_code(db: &DatabaseConnection, code: &str) -> Result<Language> {
        let language = language::Entity::find()
            .filter(language::Column::Code.eq(code))
            .one(db)
            .await?
            .ok_or_else(|| AppError::Validation(format!("Language with code '{}' not found", code)))?;

        Ok(Language {
            id: language.id,
            code: language.code,
            name: language.name,
            is_default: language.is_default,
            is_active: language.is_active,
        })
    }

    pub async fn list(
        db: &DatabaseConnection,
        options: FilterOptions,
    ) -> Result<PaginatedResponse<Language>> {
        let page = options.page.unwrap_or(1);
        let per_page = options.per_page.unwrap_or(20).min(100);
        let offset = (page - 1) * per_page;

        let mut query = language::Entity::find();

        if let Some(is_active) = options.status {
            let active = matches!(is_active, crate::models::PublicationStatus::Published);
            query = query.filter(language::Column::IsActive.eq(active));
        }

        let total = query.clone().count(db).await?;
        let languages = query
            .order_by_asc(language::Column::Code)
            .offset(offset as u64)
            .limit(per_page as u64)
            .all(db)
            .await?;

        let data = languages
            .into_iter()
            .map(|lang| Language {
                id: lang.id,
                code: lang.code,
                name: lang.name,
                is_default: lang.is_default,
                is_active: lang.is_active,
            })
            .collect();

        Ok(PaginatedResponse {
            data,
            pagination: Pagination {
                page,
                per_page,
                total,
                total_pages: (total as f64 / per_page as f64).ceil() as u32,
            },
        })
    }

    pub async fn update(
        db: &DatabaseConnection,
        id: Uuid,
        request: UpdateLanguageRequest,
    ) -> Result<Language> {
        let language = language::Entity::find_by_id(id)
            .one(db)
            .await?
            .ok_or_else(|| AppError::NotFound {
                entity: "Language".to_string(),
                id,
            })?;

        let was_default = language.is_default;
        let mut active_model: language::ActiveModel = language.into();

        if let Some(name) = request.name {
            active_model.name = Set(name);
        }

        if let Some(is_default) = request.is_default {
            if is_default && !was_default {
                // Unset other default languages
                language::Entity::update_many()
                    .filter(language::Column::IsDefault.eq(true))
                    .col_expr(language::Column::IsDefault, Expr::value(false))
                    .exec(db)
                    .await?;
            }
            active_model.is_default = Set(is_default);
        }

        if let Some(is_active) = request.is_active {
            active_model.is_active = Set(is_active);
        }

        active_model.updated_at = Set(chrono::Utc::now());

        let updated = active_model.update(db).await?;

        Ok(Language {
            id: updated.id,
            code: updated.code,
            name: updated.name,
            is_default: updated.is_default,
            is_active: updated.is_active,
        })
    }

    pub async fn delete(db: &DatabaseConnection, id: Uuid) -> Result<()> {
        let language = language::Entity::find_by_id(id)
            .one(db)
            .await?
            .ok_or_else(|| AppError::NotFound {
                entity: "Language".to_string(),
                id,
            })?;

        if language.is_default {
            return Err(AppError::Validation("Cannot delete default language".to_string()));
        }

        language::Entity::delete_by_id(id).exec(db).await?;
        Ok(())
    }

    pub async fn get_active_languages(db: &DatabaseConnection) -> Result<Vec<Language>> {
        let languages = language::Entity::find()
            .filter(language::Column::IsActive.eq(true))
            .order_by_asc(language::Column::Code)
            .all(db)
            .await?;

        Ok(languages
            .into_iter()
            .map(|lang| Language {
                id: lang.id,
                code: lang.code,
                name: lang.name,
                is_default: lang.is_default,
                is_active: lang.is_active,
            })
            .collect())
    }

    pub async fn get_default_language(db: &DatabaseConnection) -> Result<Language> {
        let language = language::Entity::find()
            .filter(language::Column::IsDefault.eq(true))
            .filter(language::Column::IsActive.eq(true))
            .one(db)
            .await?
            .ok_or_else(|| AppError::Validation("No default language found".to_string()))?;

        Ok(Language {
            id: language.id,
            code: language.code,
            name: language.name,
            is_default: language.is_default,
            is_active: language.is_active,
        })
    }
}